import pandas as pd
import numpy as np
import cv2
from matplotlib import pyplot as plt
import matplotlib.patches as patches
from sklearn.model_selection import train_test_split
import shutil
import os
import sklearn
from sklearn.metrics import confusion_matrix
import itertools
import random
PATH = 'D:/Colab Notebooks/'
PATH_IMG = PATH + 'connected_images/'
Посмотрим на изображения и csv файл
test = pd.read_csv(PATH + 'test.csv')
test_encoded = pd.read_csv(PATH + 'test_encoded.csv')
train = pd.read_csv(PATH + 'train.csv')
train_encoded = pd.read_csv(PATH + 'train_encoded.csv')
data = {'test': test, 'test_encoded': test_encoded, 'train': train, 'train_encoded': train_encoded}
print(f"Объем обучающей выборки = {len(train)} \nОбъем тестовой выборки = {len(test)}")
Объем обучающей выборки = 2541 Объем тестовой выборки = 1688
train.head()
| Unnamed: 0 | image_name | type | xmin | xmax | ymin | ymax | |
|---|---|---|---|---|---|---|---|
| 0 | 0 | 0007Date_01_08_2019.jpg | other | 285 | 368 | 61 | 278 |
| 1 | 1 | 0013Date_01_08_2019.jpg | armature | 187 | 550 | 101 | 253 |
| 2 | 2 | 0016Date_01_08_2019.jpg | armature | 172 | 327 | 13 | 360 |
| 3 | 3 | 0019Date_01_08_2019.jpg | armature | 19 | 267 | 162 | 237 |
| 4 | 4 | 0019Date_01_08_2019.jpg | armature | 309 | 548 | 300 | 376 |
train_encoded.head()
| Image_name_Type | EncodedPixels | |
|---|---|---|
| 0 | 0007Date_01_08_2019.jpg_armature | 1 1 |
| 1 | 0007Date_01_08_2019.jpg_other | 43229 83 43933 83 44637 83 45341 83 46045 83 4... |
| 2 | 0007Date_01_08_2019.jpg_wood | 1 1 |
| 3 | 0013Date_01_08_2019.jpg_armature | 71291 363 71995 363 72699 363 73403 363 74107 ... |
| 4 | 0013Date_01_08_2019.jpg_other | 1 1 |
Посмотрим, есть ли пропуски в данных
def check_empty(table):
headers = table.columns
count_null = pd.DataFrame(table[headers].isnull().sum())
count_null = count_null.reset_index()
count_null.rename(columns = {'index' : 'Column', 0 : 'Count_null'}, inplace = True)
return count_null
for key in data:
print(key + ':')
print(check_empty(data[key]), '\n\n')
test:
Column Count_null
0 Unnamed: 0 0
1 image_name 0
2 type 0
3 xmin 0
4 xmax 0
5 ymin 0
6 ymax 0
7 Usage 0
test_encoded:
Column Count_null
0 Image_name_Type 0
1 EncodedPixels 0
2 Usage 0
train:
Column Count_null
0 Unnamed: 0 0
1 image_name 0
2 type 0
3 xmin 0
4 xmax 0
5 ymin 0
6 ymax 0
train_encoded:
Column Count_null
0 Image_name_Type 0
1 EncodedPixels 0
Пропусков нет
Посмотрим на соотношение размеров классов
h = train['type'].hist()
fig = h.get_figure()
Как видно, выборка не сбалансирована
В идеале, нужно применить сэмплинг
Посмторим на экземпляры классов и выделенные области
pic_box = plt.figure(figsize=(50,50))
i = 0
for t in ['armature', 'wood', 'other']:
for _, row in train[train['type'] == t].sample(3).iterrows():
img = cv2.imread(os.path.join(PATH_IMG, row['image_name']))
img = cv2.rectangle(img, (row['xmin'], row['ymin']), (row['xmax'], row['ymax']), (0, 255, 0), 2)
pic_box.add_subplot(3, 3, i+1)
plt.text(0, 0, t, fontsize=35, color='black')
plt.imshow(img)
plt.axis('off')
i+=1
plt.show()
Данный набор отлично подходит для задачи Object Detection (мне стало интересно и я ее сделала в конце).
Для решения поставленной задачи необходимо дополнительно разметить изображения для Instance Segmentation
Размер выоборок
Я поделила изображения в следующем соотношении:
В классе other всего 168 изображений, поэтому я использовала аугментацию
Результат сохранен в папку dataset, сама разметка хранится в файле via_redion_data.json
Я использовала модель Mask R-CNN, с помощью которой можно получить необходимые маски
# метрики, полученные в ходе обучения
%reload_ext tensorboard
%tensorboard --logdir logs/custom3 --port 8088
Reusing TensorBoard on port 8088 (pid 4644), started 0:01:19 ago. (Use '!kill 4644' to kill it.)
На выходе получился довольно большой loss, но это оправдано размером обучающей выборки
from mrcnn.mrcnn_colab_engine import *
import cv2
import warnings
warnings.filterwarnings('ignore')
import os
import sys
# Import Mask RCNN
import numpy as np
from skimage.measure import find_contours
from mrcnn.config import Config
import mrcnn.model as modellib
import colorsys
import random
import cv2
class InferenceConfig(Config):
GPU_COUNT = 1
IMAGES_PER_GPU = 1
NUM_CLASSES = 4
DETECTION_MIN_CONFIDENCE = 0.9
NAME = 'ore'
# загрузим веса
MODEL_DIR = "logs/custom3/mask_rcnn_experiment_0040.h5"
config = InferenceConfig()
model = modellib.MaskRCNN(mode="inference", model_dir="logs/custom3", config=config)
model.load_weights(MODEL_DIR, by_name=True)
# добавим классы
class_names = ["BG", "armature", "wood", "other"]
# гнерация цветов
colors = None
Выделим объекты на изображении
img = cv2.imread("12812Date_06_08_2019.jpg")
plt.imshow(img)
<matplotlib.image.AxesImage at 0x1d89a7189b0>
results = model.detect([img], verbose=1)
r = results[0]
new_img = visualize.display_instances(img, r['rois'], r['masks'], r['class_ids'], class_names, r['scores'])
plt.imshow(new_img)
Processing 1 images image shape: (421, 704, 3) min: 0.00000 max: 255.00000 uint8 molded_images shape: (1, 512, 512, 3) min: -123.70000 max: 149.10000 float64 image_metas shape: (1, 16) min: 0.00000 max: 704.00000 float64 anchors shape: (1, 65472, 4) min: -0.70849 max: 1.58325 float32
<matplotlib.image.AxesImage at 0x1d8993617b8>
Маска, конечно, похожа на бред. Но обучающая выборка была слишком мала (и из метрик видно большую ошибку)
Выведем чем и насколько загрязнена руда
# посчитаем площадь маски - колиество пикселей принадлежащих выделенной области
# => количесто элементов маски, равных True
def get_square(mask):
count = 0
for row in mask:
for e in row:
if (e):
count += 1
return count
# статистика загрязнения
def get_pollution_statistics(img, res):
height, width, _ = img.shape
square_img = height * width
stat = {"armature": {"px": 0, "%": 0}, "wood": {"px": 0, "%": 0}, "other": {"px": 0, "%": 0}}
for i, class_id in enumerate(res['class_ids']):
cl = class_names[class_id]
mask = res['masks'][:, :, i]
if (cl in stat.keys()):
stat[cl]["px"] += get_square(mask)
for key in stat.keys():
px = stat[key]["px"]
if (px > 0.000005):
stat[key]["%"] = px * 100 / square_img
return stat
# посмотрим чем загрязнена руда и насколько
# px - общая площадь маски, % - процентное отношение к размеру изображения
get_pollution_statistics(img, r)
{'armature': {'px': 0, '%': 0},
'wood': {'px': 0, '%': 0},
'other': {'px': 27403, '%': 9.245775750377888}}
dict_class = {'armature': "0", 'other': "1", 'wood': "2"}
PATH_LABELS = "C:/Users/User/yolov3-master/data/labels/"
PATH_IMG = "C:/Users/User/yolov3-master/data/images/"
PATH_DATA = "C:/Users/User/yolov3-master/data/"
data = {'test': test, 'train': train}
# выборка была разбита на обучающую и тестовую:
print(f"Объем обучающей выборки = {len(data['train'])};\nОбъем тестовой выборки = {len(data['test'])};")
Объем обучающей выборки = 2541; Объем тестовой выборки = 1688;
Создадим директорию с текстовыми файлами - координаты рамок для каждого изображения
Например:
0068Date_01_08_2019
2 0.590199 0.876485 0.276989 0.218527
# for key in data:
# for i, row in data[key].iterrows():
# im = cv2.imread('D:/Colab Notebooks/connected_images/' + row['image_name'])
# height, width, _ = im.shape
# name = row["image_name"].split(".")[0]
# img_class = dict_class[row["type"]]
# x_c = (row["xmin"] + row["xmax"]) / 2.0
# y_c = (row["ymin"] + row["ymax"]) / 2.0
# widht_area = row["xmax"] - row["xmin"]
# height_area = row["ymax"] - row["ymin"]
# x_cn = round(x_c / width, 6)
# y_cn = round(y_c / height, 6)
# widht_area_n = round(widht_area / width, 6)
# height_area_n = round(height_area / height, 6)
# coords = str(x_cn) + " "\
# + str(y_cn) + " "\
# + str(widht_area_n) + " "\
# + str(height_area_n)
# output_str = img_class + " " + coords
# file = open(PATH_LABELS + name + ".txt", "w+")
# file.write(output_str)
# file.close()
Обучение проводилось с параметрами epochs = 5, batch = 4.
Обучение заняло ~5 часов. Для столь небольшого времени обучения, результаты весьма неплохие.
img_labels = cv2.imread("C:/Users/User/yolov3-master/runs/train/exp33/val_batch0_labels.jpg")
img_predict = cv2.imread("C:/Users/User/yolov3-master/runs/train/exp33/val_batch0_pred.jpg")
conf_matrix = cv2.imread("C:/Users/User/yolov3-master/runs/train/exp33/confusion_matrix.png")
result = cv2.imread("C:/Users/User/yolov3-master/runs/train/exp33/results.png")
f, axarr = plt.subplots(1,2, figsize=(100, 100))
axarr[0].imshow(img_labels)
axarr[0].set_title('Исходное изображение (с обучающей разметкой)', fontsize=65)
axarr[1].imshow(img_predict)
axarr[1].set_title('Результат детектирования', fontsize=65)
Text(0.5, 1.0, 'Результат детектирования')
# матрица ошибок
f, axarr = plt.subplots(1,2, figsize=(100, 100))
axarr[0].imshow(conf_matrix)
axarr[0].set_title('Матрица ошибок', fontsize=65)
axarr[1].imshow(result)
axarr[1].set_title('Результаты по метрикам', fontsize=65)
Text(0.5, 1.0, 'Результаты по метрикам')
Как видно, у полученной модели проблемы с определением класса "other". Модель путает его с фоном.